package com.smh.demo1.a34;

import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.coyote.http11.Http11Nio2Protocol;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author shiminghui
 * @date 2025/5/8 17:34
 * @description: TODO
 */
public class A34 {
    public static void main(String[] args) throws IOException, LifecycleException {

        // 利用编程的方法启动一个Tomcat
        Tomcat tomcat = new Tomcat();

        // 设置工作目录
        tomcat.setBaseDir("tomcat");

        // 设置临时目录
        File file = Files.createTempDirectory("boot.").toFile();
        file.deleteOnExit(); // (退出时)删除临时目录

        // 添加context
        Context context = tomcat.addContext("", file.getAbsolutePath()); // 应用上下文的路径为空时代表"/",  临时目录

        WebApplicationContext applicationContext = createApplicationContext();


        context.addServletContainerInitializer(new ServletContainerInitializer() {
            @Override
            public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
                servletContext.addServlet("helloServlet", new HelloServlet()).addMapping("/hello");

                // 加入springMVC的servlet(即dispatcherServlet)
                /*DispatcherServlet dispatcherServlet = (DispatcherServlet) applicationContext.getBean("dispatcherServlet");
                System.out.println(dispatcherServlet);
                servletContext.addServlet("dispatcherServlet",dispatcherServlet).addMapping("/");*/
                // 这里有一点小问题
                // 我们spring容器里面如果有多个servlet容器怎么办
                // 这里我们如果直接按类型获取dispatcherServlet,那又有一个问题,这个路径映射怎么办
                // 所以DispatcherServletRegistrationBean的作用就体现出来了,因为这里存储了映射信息和dispatcherServlet容器
                Map<String, DispatcherServletRegistrationBean> beansOfType = applicationContext.getBeansOfType(DispatcherServletRegistrationBean.class);
                for (DispatcherServletRegistrationBean value : beansOfType.values()) {
//                    servletContext.addServlet(value.getServletName(),value.getServlet()).addMapping(value.getPath());
                    value.onStartup(servletContext);
                }

            }
        }, Collections.emptySet());

        // 设置监听端口和请求协议
        /*Connector connector = tomcat.getConnector();
        connector.setPort(8080);*/

        Connector connector = new Connector(new Http11Nio2Protocol());
        connector.setPort(8080);
        tomcat.setConnector(connector);

        // 启动Tomcat
        tomcat.start();

    }

    public static WebApplicationContext createApplicationContext() {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(Config.class);
        context.refresh();
        return context;
    }

    @Configuration
    static class Config {
        @Bean
        public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }

        @Bean
        // 这个例子中必须为 DispatcherServlet 提供 AnnotationConfigWebApplicationContext, 否则会选择 XmlWebApplicationContext 实现
        public DispatcherServlet dispatcherServlet(WebApplicationContext applicationContext) {
            return new DispatcherServlet(applicationContext);
        }

        @Bean
        public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
            RequestMappingHandlerAdapter handlerAdapter = new RequestMappingHandlerAdapter();
            handlerAdapter.setMessageConverters(List.of(new MappingJackson2HttpMessageConverter()));
            return handlerAdapter;
        }

        @RestController
        static class MyController {
            @GetMapping("hello2")
            public Map<String, Object> hello() {
                return Map.of("hello2", "hello2, spring!");
            }
        }

    }

}
